const seedrandom = require('seedrandom') // 引入 seedrandom 库

// 引入 lodash 库，用于数据处理和操作
const _ = require('lodash')
// 精确计算
const NP = require('number-precision')
// 引入 path 模块，用于处理文件和目录路径
const path = require('path')
// 获取项目根目录
const ROOT_DIR = path.join(__dirname, '..')
// helpers 目录
const HELPERS_DIR = path.join(ROOT_DIR, 'helpers')
// 读取所有数据
let { allValues } = require(path.join(HELPERS_DIR, 'readAllValues.js'))

// 适应度函数：根据历史数据中的号码频率评估个体的适应度
function fitness(individual, historicalData) {
  let fitnessScore = 0 // 初始化适应度得分

  // 计算历史数据中每个号码的频率
  let frequency = new Array(50).fill(0) // 创建一个长度为50的数组，所有元素初始化为0。 索引0不用，索引1-49对应1-49号码的频率
  historicalData.forEach((data) => {
    frequency[data.number]++ // 遍历历史数据，统计每个号码出现的次数。 data.number 表示开奖号码，直接作为数组的索引
  })

  // 计算当前个体与历史频率的匹配度
  individual.forEach((number) => {
    fitnessScore += frequency[number] || 0 // 遍历个体中的每个号码，将该号码在历史数据中的频率加到适应度得分上。 || 0 用于处理号码超出范围的情况
  })

  return fitnessScore // 返回适应度得分
}

// 生成一个不重复的个体（24个不重复的号码）
function generateUniqueIndividual(rng) {
  let availableNumbers = [...Array(49).keys()].map((i) => i + 1) // 生成[1, 2, ..., 49]
  let individual = []

  for (let i = 0; i < 24; i++) {
    const randomIndex = Math.floor(rng() * availableNumbers.length) // 随机生成一个索引
    individual.push(availableNumbers.splice(randomIndex, 1)[0]) // 将随机选中的号码从可用号码列表中移除
  }
  return individual
}

// 初始化种群
function initializePopulation(populationSize, rng) {
  let population = [] // 初始化种群数组
  for (let i = 0; i < populationSize; i++) {
    let individual = generateUniqueIndividual(rng) // 生成不重复的个体
    population.push(individual) // 将新生成的个体添加到种群数组中
  }
  return population // 返回初始化后的种群
}

// 选择操作：轮盘赌选择
function select(population, historicalData, rng) {
  let totalFitness = 0 // 初始化总适应度得分
  population.forEach((individual) => {
    totalFitness += fitness(individual, historicalData) // 计算每个个体的适应度，并累加到总适应度得分上
  })

  let randomValue = rng() * totalFitness // 生成一个0到总适应度得分之间的随机数，作为轮盘赌的指针
  let accumulatedFitness = 0 // 初始化累积适应度得分
  for (let individual of population) {
    accumulatedFitness += fitness(individual, historicalData) // 将当前个体的适应度得分累加到累积适应度得分上
    if (accumulatedFitness >= randomValue) {
      return individual // 返回被选择的个体
    }
  }
}

// 交叉操作：单点交叉
function crossover(parent1, parent2, rng) {
  let crossoverPoint = Math.floor(rng() * parent1.length) // 随机生成一个交叉点，用于分割父代基因
  let child1 = parent1.slice(0, crossoverPoint).concat(parent2.slice(crossoverPoint)) // 子代1：从父代1的起始位置到交叉点，加上父代2从交叉点到结尾
  let child2 = parent2.slice(0, crossoverPoint).concat(parent1.slice(crossoverPoint)) // 子代2：与子代1相反

  // 去重：确保交叉后的子代没有重复的号码
  child1 = [...new Set(child1)] // 将子代1去重
  child2 = [...new Set(child2)] // 将子代2去重

  // 如果去重后的子代不满足24个号码，继续从可用号码中补充
  while (child1.length < 24) {
    let availableNumbers = [...Array(49).keys()].map((i) => i + 1).filter((n) => !child1.includes(n)) // 获取子代1没有的号码
    const randomIndex = Math.floor(rng() * availableNumbers.length)
    child1.push(availableNumbers[randomIndex])
  }

  while (child2.length < 24) {
    let availableNumbers = [...Array(49).keys()].map((i) => i + 1).filter((n) => !child2.includes(n)) // 获取子代2没有的号码
    const randomIndex = Math.floor(rng() * availableNumbers.length)
    child2.push(availableNumbers[randomIndex])
  }

  return [child1, child2] // 返回两个子代
}

// 变异操作：随机变动一个位置的号码
function mutate(individual, rng) {
  let mutationPoint = Math.floor(rng() * individual.length) // 随机生成一个变异点，用于确定需要变异的基因位置
  let newNumber = Math.floor(rng() * 49) + 1 // 随机生成一个新的号码

  // 确保变异后的号码不重复
  while (individual.includes(newNumber)) {
    newNumber = Math.floor(rng() * 49) + 1
  }

  individual[mutationPoint] = newNumber // 将变异点的基因替换为新的不重复号码
  return individual // 返回变异后的个体
}

// 遗传算法主函数
function geneticAlgorithm(historicalData, populationSize = 50, generations = 100, seed = 'default-seed') {
  const rng = seedrandom(seed) // 使用 seedrandom 库创建一个固定随机种子生成器
  let population = initializePopulation(populationSize, rng) // 初始化种群，每个个体包含24个号码

  for (let gen = 0; gen < generations; gen++) {
    let newPopulation = [] // 初始化新的种群

    // 选择父代
    while (newPopulation.length < populationSize) {
      let parent1 = select(population, historicalData, rng) // 使用轮盘赌选择选择父代1
      let parent2 = select(population, historicalData, rng) // 使用轮盘赌选择选择父代2

      // 交叉操作
      let [child1, child2] = crossover(parent1, parent2, rng) // 使用单点交叉生成两个子代

      // 变异操作
      child1 = mutate(child1, rng) // 对子代1进行变异
      child2 = mutate(child2, rng) // 对子代2进行变异

      newPopulation.push(child1) // 将子代1添加到新种群
      newPopulation.push(child2) // 将子代2添加到新种群
    }

    population = newPopulation.slice(0, populationSize) // 更新种群，截取前 populationSize 个个体，保持种群大小不变
  }

  // 返回最适应的个体
  let bestIndividual = population.reduce((best, current) => {
    return fitness(current, historicalData) > fitness(best, historicalData) ? current : best
  })

  return bestIndividual // 返回最适应的个体
}

const count = 111

// 示例：调用遗传算法进行预测
const historicalData = allValues.slice(0, count) // 获取历史数据
const predictedNumbers = geneticAlgorithm(historicalData, 50, 100, 'my-seed') // 调用遗传算法进行预测
console.log(
  'Predicted Numbers:',
  predictedNumbers.sort((a, b) => a - b)
) // 输出预测的号码
console.log(allValues[count]) // 输出某一特定历史数据
